package models

// SDK官方指南https://docs.docker.com/engine/api/sdk/examples/

import (
	"context"
	"encoding/base64"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"github.com/docker/docker/pkg/stdcopy"
	containertypes "github.com/docker/docker/api/types/container"
	"github.com/docker/go-connections/nat"
	"io"
	"os"
	"encoding/json"
)

// AuthConfig contains authorization information for connecting to a Registry
type AuthConfig struct {
	Username string `json:"username,omitempty"`
	Password string `json:"password,omitempty"`
	Auth     string `json:"auth,omitempty"`

	// Email is an optional value associated with the username.
	// This field is deprecated and will be removed in a later
	// version of docker.
	Email string `json:"email,omitempty"`

	ServerAddress string `json:"serveraddress,omitempty"`

	// IdentityToken is used to authenticate the user and get
	// an access token for the registry.
	IdentityToken string `json:"identitytoken,omitempty"`

	// RegistryToken is a bearer token to be sent to a registry
	RegistryToken string `json:"registrytoken,omitempty"`
}

func Init(){
	//1、判断基础服务docker 是否存在,不存在启动重新安装程序
	//beego.Run()

	//2、如果存在判断是否启动

	//3、如果没有启动开始启动

	//4、如果启动了，自动打开浏览器页面 for windows
}


func getVersion() string{
	cli, err := client.NewClientWithOpts(client.WithVersion("1.38"))
	if err != nil {
		panic(err)
	}
	version :=  cli.ClientVersion()
	return version
}


func GetClient() (*client.Client,context.Context){
	ctx := context.Background()
	cli, err := client.NewClientWithOpts(client.WithVersion("1.38"))
	if err != nil {
		panic(err)
	}
	config := types.AuthConfig{
		Username: "renjie59420",
		Password: "rj123456",
		ServerAddress: "registry.cn-shenzhen.aliyuncs.com",
	}
	response, err := cli.RegistryLogin(ctx, config)
	//expected := "Error response from daemon: Get https://registry-1.docker.io/v2/: unauthorized: incorrect username or password"
	if err != nil {
		panic(err)
	}
	beego.Debug(response)
	return cli,context.Background()
}




func Login(Username string,Password string,remoteAddress string ){

}


//阿里云不可用，github可以

//func Login( ){
//	ctx := context.Background()
//	cli, err := client.NewClientWithOpts(client.WithVersion("1.38"))
//	if err != nil {
//		panic(err)
//	}
//	config := types.AuthConfig{
//		Username: "renjie59420",
//		Password: "rj123456",
//		ServerAddress: "registry.cn-shenzhen.aliyuncs.com",
//	}
//	response, err := cli.RegistryLogin(ctx, config)
//	//expected := "Error response from daemon: Get https://registry-1.docker.io/v2/: unauthorized: incorrect username or password"
//	if err != nil {
//		panic(err)
//	}
//	beego.Debug(response)
//}

// 运行一个容器，相当于docker run alpine echo hello world
func CreateContainer() {
	cli, ctx := GetClient()
	reader, err := cli.ImagePull(ctx, "docker.io/library/alpine", types.ImagePullOptions{})
	if err != nil {
		panic(err)
	}
	io.Copy(os.Stdout, reader)

	resp, err := cli.ContainerCreate(ctx, &container.Config{
		Image: "alpine",
		Cmd:   []string{"echo", "hello world"},
		Tty:   true,
	}, nil, nil, "")
	if err != nil {
		panic(err)
	}

	if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
		panic(err)
	}

	statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
	select {
	case err := <-errCh:
		if err != nil {
			panic(err)
		}
	case <-statusCh:
	}

	out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})
	if err != nil {
		panic(err)
	}

	stdcopy.StdCopy(os.Stdout, os.Stderr, out)
}

// 启动
func StartContainers(containerID string) {
	cli, _ := GetClient()

	err := cli.ContainerStart(context.Background(), containerID, types.ContainerStartOptions{})

	if err == nil {
		fmt.Println("容器", containerID, "启动成功")
	}else{
		log(err)
	}
}


func PullImages(imageName string){

	cli, ctx := GetClient()
	username,password :=GetNameAndPassword()

	jsonBytes, _ := json.Marshal(map[string]string{
		"username":  username,"password": password})

	opts := types.ImagePullOptions{
		RegistryAuth: base64.StdEncoding.EncodeToString(jsonBytes)}

	reader, err := cli.ImagePull(ctx, imageName, opts)
	if err != nil {
		panic(err)
	}
	io.Copy(os.Stdout, reader)
}

// 删除
func removeContainers(containerID string, cli *client.Client) (string, error) {
	err := cli.ContainerRemove(context.Background(), containerID, types.ContainerRemoveOptions{})
	log(err)
	return containerID, err
}



func  CreateContainers(imageName string) (string,error){
	cli, ctx := GetClient()
	exposedPorts, portBindings, _ := nat.ParsePortSpecs([]string{
		"5200:5200/tcp",
		//"5201:5201/tcp",
		//"5202:5202/tcp",
		//"5203:5203/tcp",
		"5204:5204/tcp",
		"5205:5205/tcp",
		"5206:5206/tcp",
		"5207:5207/tcp",
		"5208:5208/tcp",
		"5209:5209/tcp",
		"5210:5210/tcp",
		"5211:5211/tcp",
		"5212:5212/tcp",
	})
	//if _, ok := portMap[Port("5210/tcp")]; !ok {
	//	t.Fatal("3456/sctp was not parsed properly")
	//}

	config := &container.Config{Image: imageName, ExposedPorts: exposedPorts,Cmd:   []string{"bash","/mnt/start.sh"}}

	hostConfig := containertypes.HostConfig{
		PortBindings:portBindings,
		//Resources: containertypes.Resources{
		//	CPUShares:  512,
		//	CpusetCpus: "0",
		//},
	}

	containerName := "uniappadmin"
	containerId,err:=FindContainerIdByname(containerName)
	if containerId !=""{
		if err := cli.ContainerStop(ctx, containerId, nil); err != nil {
			beego.Debug(err)
		}
		cli.ContainerRemove(context.Background(), containerId, types.ContainerRemoveOptions{})
	}
	body, err := cli.ContainerCreate(context.Background(), config, &hostConfig, nil, containerName)

	fmt.Printf("ID: %s\n", body.ID)
	return body.ID,err
}



// 后台运行容器，相当于键入 docker run -d bfirsh/reticulate-splines
func CreateContainerInBackground(imageName string)  {


	cli, ctx := GetClient()
	exposedPorts, portBindings, _ := nat.ParsePortSpecs([]string{
		"127.0.0.1:5200:5200",
		"127.0.0.1:5201:5201",
		"127.0.0.1:5202:5202",
		"127.0.0.1:5203:5203",
		"127.0.0.1:5204:5204",
		"127.0.0.1:5205:5205",
		"127.0.0.1:5206:5206",
		"127.0.0.1:5207:5207",
		"127.0.0.1:5208:5208",
		"127.0.0.1:5209:5209",
		"127.0.0.1:5210:5210",
		"127.0.0.1:5211:5211",
		"127.0.0.1:5212:5212",
	})
	hostConfig := containertypes.HostConfig{
		PortBindings:portBindings,
		//Resources: containertypes.Resources{
		//	CPUShares:  512,
		//	CpusetCpus: "0",
		//},
	}



	resp, err := cli.ContainerCreate(ctx, &container.Config{
		Image: imageName,
		ExposedPorts: exposedPorts,// it supposed to be nat.PortSet
		Cmd:   []string{},
	}, &hostConfig, nil, "")
	if err != nil {
		panic(err)
	}
	beego.Debug( resp)

	if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
		panic(err)
	}

	fmt.Println(resp.ID)
}

// 列出所有镜像
func ListImages() {
	//ctx := context.Background()
	cli, err := client.NewClientWithOpts(client.WithVersion("1.38"))
	if err != nil {
		panic(err)
	}

	containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{})
	if err != nil {
		panic(err)
	}

	for _, container := range containers {
		fmt.Println(container.ID)
	}
}

// 打印特定容器的日志
func LogsByContainerId() {
	cli, ctx := GetClient()


	options := types.ContainerLogsOptions{ShowStdout: true}
	// Replace this ID with a container that really exists
	out, err := cli.ContainerLogs(ctx, "f1064a8a4c82", options)
	if err != nil {
		panic(err)
	}

	io.Copy(os.Stdout, out)
}


// 根据容器名称查找容器id
func FindContainerIdByname(name string)(id string, err error) {
	cli, _ := GetClient()

	containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true})
	if err != nil {
		panic(err)
	}

	for _, container := range containers {
		beego.Debug(container.Names)
		if container.Names[0] =="/"+name || container.Names[0] ==name{
			beego.Debug("find name")
			return container.ID,err
		}
		fmt.Println(container.ID)
		// 停止容器

		// 删除容器
		//cli.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{})

	}
	beego.Debug("do not find name")
	return "",err
}
// 列出并管理容器
func ListAndManageContainer() {
	ctx := context.Background()
	cli, err := client.NewClientWithOpts(client.WithVersion("1.38"))
	if err != nil {
		panic(err)
	}

	containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{})
	if err != nil {
		panic(err)
	}

	for _, container := range containers {
		fmt.Println(container.ID)
		// 停止容器
		if err := cli.ContainerStop(ctx, container.ID, nil); err != nil {
			panic(err)
		}
		// 删除容器
		//cli.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{})
	}
}